package Controller;

import java.util.Random;

import javax.swing.SwingUtilities;

import Controller.EstandarxDLG;
import Model.twoWays;
/**
 * NOTA:
 * Para dar los parametros del AG: se accede directamente a los campos publicos.
 */
public abstract class AlgoritmoGenetico implements Runnable{
	public  double la1;
	public  double la2;
	public  double la3;
	public  double la4;

	public double mejorcromo[];
	boolean salio=false;
	// parametros AG
	public double probabilidadCruce;
	public double probabilidadMutacion;
	public int generaciones;
	// numero de cromosomas es la poblacion,!=tama�o de la muestra
	public int poblacion;
	protected  double[][] cromosomas;	// 4 lambdas
	protected  double[] rendimiento;
	protected  int indiceMejorCromosoma;
	public double valorMin;			// da el rango de valores para la poblacion inicial
	public double valorMax;
	// parametros moduloDLG
	public  double muestra[];

	// criterio de parada
	public  double errorMax;
	public  double errorMaxDePoblacion=Double.MAX_VALUE;
	// aleatorio
	protected Random rd = new Random();

	EstandarxDLG panelPrincipal;

	
	public AlgoritmoGenetico()	{
		this.probabilidadCruce = 0.9d;
		this.probabilidadMutacion = 0.4d;
		this.poblacion = 100;
		this.generaciones = 50;
		this.errorMax=1e-15;
		this.valorMax=1000;
		this.valorMin=-1000;
	}
	
	public AlgoritmoGenetico(EstandarxDLG estandarxDLG) {
		this.panelPrincipal=estandarxDLG;
		this.probabilidadCruce = 0.9d;
		this.probabilidadMutacion = 0.4d;
		this.poblacion = 100;
		this.generaciones = 50;
		this.errorMax=1e-15;
		this.valorMax=1000;
		this.valorMin=-1000;
	}

	public void run() {
		panelPrincipal.progreso.inicio();//barra de progreso %, y forma de cancelar
		String e = "";		
		poblacionInicial();
		calcularRendimientoIndividuos();
		ordenarPorRendimiento();
                e = "maxp real:\t"+errorMaxDePoblacion;
//                twoWays.setArchivo(twoWays.getArchivo()+" \n"+e);
                Archivos.escrituraArchivos(e);
		System.out.println("maxp real:\t"+errorMaxDePoblacion);	
                e = "error teorico:\t"+ errorMax;
//                twoWays.setArchivo(twoWays.getArchivo()+" \n"+e);
                Archivos.escrituraArchivos(e);
		System.out.println("error teorico:\t"+ errorMax);

		//**
		//Ahora el for se ejecuta tomando en encuenta tambien el error
		for (int i = 0; errorMaxDePoblacion>errorMax && i < generaciones && !Thread.interrupted(); i++) {
			ordenarPorRendimiento();
			evolucionarPoblacion();
			generarMutaciones();
			calcularRendimientoIndividuos();

			//**
			//cada vez que se calcula el rendimiento hay que volver a ordenar
			ordenarPorRendimiento();
			//**
			//despues de ordenar siempre le mejor estara en la posicion 0
			errorMaxDePoblacion = rendimiento[0];

			int porcent=((i + 1) * 100)/(generaciones);
			int seg=(generaciones-i)*poblacion/32;

			panelPrincipal.progreso.tiempoTxt.setText(seg+" segundos");
			panelPrincipal.progreso.progressBar.setValue(porcent);
			//se puede incluir el caso en que se suspenda la iteracion y se guarde en la BD
			String cons="Error="+errorMaxDePoblacion+"\tf(x)="+rendimiento[0];
			//panelPrincipal.progreso.consola.setText(cons);//<--
                        e =  porcent +"% ("+seg+") seg	"+cons;
			System.out.println(porcent +"% ("+seg+") seg	"+cons);
                        Archivos.escrituraArchivos(e);
//                        twoWays.setArchivo(twoWays.getArchivo()+" \n"+e);
		}
		//Guardar_BD(muestra,L1,L2,L3,L4,error);
		salio=false;
		//**
		// mejor rendimeinto
		errorMaxDePoblacion=rendimiento[0];
		
		
		la1=cromosomas[0][0];
		la2=cromosomas[0][1];
		la3=cromosomas[0][2];
		la4=cromosomas[0][3];
                MainController.lambda1Est = la1;
                MainController.lambda2Est = la2;
                MainController.lambda3Est = la3;
                MainController.lambda4Est = la4;
                e = " mejor: "+errorMaxDePoblacion;
		System.out.println(" mejor: "+errorMaxDePoblacion);
//                twoWays.setArchivo(twoWays.getArchivo()+" \n"+e);
                Archivos.escrituraArchivos(e);
		panelPrincipal.progreso.fin();
	}
	/**
	 * Esta funcion depende del metodo utilizado 
	 */
	protected abstract double calcularRendimiento(double[] cromo);
	/**
	 * Esta funcion depende del metodo utilizado 
	 */
	protected abstract double calcularError();

	protected  void calcularRendimientoIndividuos() {
		indiceMejorCromosoma = 0;
		double mejor = Double.MAX_VALUE;
		double rendimientoi;
		double cromo[];

		if(!salio)
			crearRendimientos();
		salio=true;
		for (int i = 0;  i < poblacion; i++) {

			cromo = cromosomas[i];

			rendimientoi = calcularRendimiento(cromo);

			if (rendimientoi < mejor) {
				mejor = rendimientoi;
				indiceMejorCromosoma = i;
			}
			rendimiento[i]=rendimientoi;

			calcularError();
			//if(errori<errorMaxDePoblacion)
			//	errorMaxDePoblacion=errori;
		}
	}

	private void crearRendimientos() {
		//if(rendimiento==null || poblacion!=rendimiento.length){
		if(rendimiento==null || salio==false ){
			rendimiento=new double[poblacion];
		}
	}

	/**
	 * crea poblacion
	 */
	protected  void poblacionInicial() {
		cromosomas=new double[poblacion][4];
		// Generar cada individuo
		for (int i = 0; i < poblacion; i++) {

			double[] cromo = new double[4];
			for (int j = 0; j < cromo.length; j++) {

				cromo[j] = (valorMax- valorMin) * rd.nextDouble() + valorMin;
			}
			cromosomas[i]=cromo;
		}
	}

	protected  void generarMutaciones() {
		for (int i = 0; i < poblacion; i++) {
			if (rd.nextDouble() <= probabilidadMutacion) {
				double[] individuo = cromosomas[i];
				int j = (int) (4 * rd.nextDouble());
				double ch = individuo[j] * (rd.nextDouble() - 0.5d) / 10;
				individuo[j] += ch;//le suma a lambda +/- 5%
			}
		}
	}

	protected  void evolucionarPoblacion() {
		int cant = (int) (probabilidadCruce * poblacion);//# de indiv a evolucionar
		int cp = poblacion -1;
		int j=0;

		//**
		// triple cruce (tres hijos)
		for (int i = 0; i < ((int)(cant/3)+1); i=i+3,j++) {
			cromosomas[cp-i] = cruzar( cromosomas[j], cromosomas[j + 1] );
			cromosomas[cp-i-1] = cruzar2( cromosomas[j], cromosomas[j + 1] );
			cromosomas[cp-i-2] = cruzar3( cromosomas[j], cromosomas[j + 1] );
		}

	}
	/**
	 * ordena de mayor a menor fitness
	 */

	//**
	//ordena el cromosoma y su correspondiente fitness en el array de rendimiento
	protected  void ordenarPorRendimiento() {
		for (int i = 0; i < poblacion - 1; i++) {
			//System.out.println("ordenar rendimeinto poblacion :"+i);
			if (rendimiento[i] > rendimiento[i + 1]) {
				double[] temp = cromosomas[i];
				double  temp2 = rendimiento[i];
				cromosomas[i] = cromosomas[i + 1];
				rendimiento[i] = rendimiento[i+1];
				cromosomas[i + 1] = temp;
				rendimiento[i+1] = temp2;
				if (i > 0) {
					i -= 2;
				}
			}
		}

		//despues de ordenar el mejor siempre estara en la casilla 0
		mejorcromo=cromosomas[0];
		indiceMejorCromosoma =0;
	}

	/**
	 * @param cromosoma
	 * @param cromosoma2
	 * @return cromosomaCruce
	 */
	protected  double[] cruzar(double[] cromosoma, double[] cromosoma2) {
		double cromosomaCruce[] = new double[4];
		for (int i = 0; i < 4; i++) {
			cromosomaCruce[i] = (cromosoma[i] + cromosoma2[i]) / 2;
		}
		return cromosomaCruce;
	}


	//nuevo crue

	protected  double[] cruzar2(double[] cromosoma, double[] cromosoma2) {
		double cromosomaCruce[] = new double[4];

		cromosomaCruce[0] = (cromosoma[0] ) ;
		cromosomaCruce[1] = (cromosoma2[1] ) ;
		cromosomaCruce[2] = (cromosoma[2] ) ;
		cromosomaCruce[3] = (cromosoma2[3] ) ;
		return cromosomaCruce;
	}

	//nuevo cruce
	protected  double[] cruzar3(double[] cromosoma, double[] cromosoma2) {
		double cromosomaCruce[] = new double[4];

		cromosomaCruce[0] = (cromosoma2[0] ) ;
		cromosomaCruce[1] = (cromosoma[1] ) ;
		cromosomaCruce[2] = (cromosoma2[2] ) ;
		cromosomaCruce[3] = (cromosoma[3] ) ;

		return cromosomaCruce;
	}
}
